module Memory (
	input	clock ,
	input	reset ,
	// Execute
	input	[2:0]	ex_mem_msm ,
	input	[2:0]	ex_mem_msl ,
	input	ex_mem_readmem ,
	input	ex_mem_writemem ,
	input	ex_mem_mshw ,
	input	ex_mem_lshw ,
	input	[31:0]	ex_mem_regb ,
	input	[2:0]	ex_mem_selwsource ,
	input	[4:0]	ex_mem_regdest ,
	input	ex_mem_writereg ,
	input	[31:0]	ex_mem_aluout ,
	input	[31:0]	ex_mem_wbvalue ,
	// Memory Controller
	output	mem_mc_rw ,
	output	mem_mc_en ,
	output	[31:0]	mem_mc_addr ,
	inout	[31:0]	mem_mc_data ,
	output	mem_mc_mshw ,
	output	mem_mc_lshw ,
	// Forwarding
	output	[31:0] mem_fw_wbvalue ,
	output	mem_fw_writereg ,
	//  Writeback
	output reg	[4:0]	mem_wb_regdest ,
	output reg	mem_wb_writereg ,
	output reg	[31:0]	mem_wb_wbvalue ) ;

    wire [31:0] mux_loadregb;
    wire [31:0] loadval;
    wire [31:0] mux_ldrbexval;
    
    //Forwarded signals
    assign mem_mc_mshw = ex_mem_mshw;
    assign mem_mc_lshw = ex_mem_lshw;
    assign mem_mc_addr = ex_mem_aluout;
    assign mem_fw_wbvalue = mux_ldrbexval;
    assign mem_fw_writereg = ex_mem_writereg;
    
    //Memory access signals
    assign mem_mc_rw = (~ex_mem_readmem & ex_mem_writemem);
    assign mem_mc_en = (ex_mem_readmem | ex_mem_writemem);

    //mem_mc_data[31:16]
    assign mem_mc_data [31:16] = (ex_mem_writemem && (ex_mem_msm == 0)) ? 16'b0 : 16'bz;
    assign mem_mc_data [31:16] = (ex_mem_writemem && (ex_mem_msm == 1)) ? ex_mem_regb[31:16] : 16'bz;
    assign mem_mc_data [31:16] = (ex_mem_writemem && (ex_mem_msm == 2)) ? ex_mem_regb[15:0] : 16'bz;
    assign mem_mc_data [31:16] = (ex_mem_writemem && (ex_mem_msm == 3)) ? {16{ex_mem_regb[15]}} : 16'bz;
    assign mem_mc_data [31:16] = (ex_mem_writemem && (ex_mem_msm == 4)) ? {16{ex_mem_regb[7]}} : 16'bz;
    //mem_mc_data[15:0]
    assign mem_mc_data [15:0] = (ex_mem_writemem && (ex_mem_msl == 0)) ? 16'b0 : 16'bz;
    assign mem_mc_data [15:0] = (ex_mem_writemem && (ex_mem_msl == 1)) ? ex_mem_regb[31:16] : 16'bz;
    assign mem_mc_data [15:0] = (ex_mem_writemem && (ex_mem_msl == 2)) ? ex_mem_regb[15:0] : 16'bz;
    assign mem_mc_data [15:0] = (ex_mem_writemem && (ex_mem_msl == 3)) ? {{8{ex_mem_regb[7]}},ex_mem_regb[7:0]} : 16'bz;

    //mux_loadregb[31:16]
    assign mux_loadregb[31:16] = (ex_mem_mshw) ? loadval[31:16] : ex_mem_regb[31:16];
    //mux_load_regb[15:0]
    assign mux_loadregb[15:0] = (ex_mem_lshw) ? loadval[15:0] : ex_mem_regb[15:0];

    //loadval[31:16]
    assign loadval[31:16] = (ex_mem_msm == 0) ? 16'b0 : 16'bz;
    assign loadval[31:16] = (ex_mem_msm == 1) ? mem_mc_data[31:16] : 16'bz;
    assign loadval[31:16] = (ex_mem_msm == 2) ? mem_mc_data[15:0] : 16'bz;
    assign loadval[31:16] = (ex_mem_msm == 3) ? {16{mem_mc_data[31]}} : 16'bz;
    //loadval[15:0]
    assign loadval[15:0] = (ex_mem_msl == 0) ? 16'b0 : 16'bz;
    assign loadval[15:0] = (ex_mem_msl == 1) ? mem_mc_data[31:16] : 16'bz;
    assign loadval[15:0] = (ex_mem_msl == 2) ? mem_mc_data[15:0] : 16'bz;
    assign loadval[15:0] = (ex_mem_msl == 3) ? {{8{mem_mc_data[31]}},mem_mc_data[31:24]} : 16'bz;
    assign loadval[15:0] = (ex_mem_msl == 4) ? {{8'b0},mem_mc_data[31:24]} : 16'bz;

    //mux_ldrbexval
    assign mux_ldrbexval = (~ex_mem_selwsource[2] & ~ex_mem_selwsource[1] & ex_mem_selwsource[0]) ? mux_loadregb : ex_mem_wbvalue;

	always @(negedge(reset)) begin
	    mem_wb_regdest <= 5'b0;
	    mem_wb_writereg <= 1'b0;
	    mem_wb_wbvalue <= 32'b0;
	end


	always @(negedge(clock)) begin
        //mem_wb_wbvalue
        mem_wb_wbvalue <= mux_ldrbexval;
        mem_wb_regdest <= ex_mem_regdest;
	      mem_wb_writereg <= ex_mem_writereg;
	end

endmodule
